////////////////////////////////////////////////////////////////////////////////
// Author: Healer
// Email: healer_kx@163.com
// 
var hj = hj || {};
var HJ = hj;

var $strings = {};
var $fragments = $fragments || {};
var $groupName = $config['group-name'];

// Mainly for ThinkPHP.
HJ.ACTIONS_PATH             = "/index.php/";

HJ.COMMON_WIDGETS_PATH      = "Tpl/Widgets";
HJ.COMMON_VIEWS_PATH        = "Tpl/Views";

HJ.WIDGETS_PATH             = "/Tpl/" + $groupName + "/Widgets/";
HJ.VIEWS_PATH               = "/Tpl/" + $groupName + "/Views/";


var $log = function(m){
	console.debug(m);
};

/*
var s = ns.split(/\./);
var p = window;
for(var i = 0; i < s.length - 1; i++) {
  p = p[s[i]] = p[s[i]] || {};
}
p[s[s.length - 1]] = function() {};
*/

/*******************************************************************************
 * usage:   $class("Derived-Class-Name", BaseClass | [BaseClass, Mixin1, ...], {
 *              constructor: function() {
 *              },
 *          });
 * 
 *
 */
function $class(d, b, p)
{
    b = b || Base;
    if ( !$class._constructorsCallStack )
    {
        $class._constructorsCallStack = function(className) {
            var p = window;
            var clz = p[className];
            if (clz != null) {
                var superClassName = clz._superClassName;
                if (superClassName)
                {
                    var line = clz._className + ".prototype.constructor.apply(this, arguments);";
                    return $class._constructorsCallStack(superClassName) + line;
                }
                else
                {
                    return "Base.prototype.constructor.apply(this, arguments);";
                }
            }
            return "";
        };
    }

    if ( typeof(d) === "string" )
    {
        var superClassName = null;
        if (b)
        {
            if (b instanceof Array)
            {
                superClassName = b[0]._className
            }
            else
            {
                superClassName = b._className;
            }
        }
        var dn = d;
        var $_super_class_ctors_call_stack = $class._constructorsCallStack(superClassName);
        
        var lines = [
                    'this._className = "' + dn + '";',
                    $_super_class_ctors_call_stack,
                    'this.constructor.apply(this, arguments);',

    				];
    	var code = lines.join('');
    	d = this[dn] = new Function(code);
        d._className = dn;
        d._superClassName = superClassName;

    }
    else if ( typeof(d) === 'function' )
    {
    }

    if (b)
    {
        if (b instanceof Array)
        {
            $.each(b, function(index){
            	$.each(b[index].prototype, function(i, v){
              		d.prototype[i] = v;
         		});
       		});
        }
        else
        {
            $.each(b.prototype, function(i, v){
          		d.prototype[i] = v;
          	});
        }
    }

    $.each(p, function(i, v){
		d.prototype[i] = v;
  	});

}

function $empty(string)
{
	return string == undefined || string == null || string.length == 0;
}

function $random(n)
{
    n = n || 4;
    var rdm = "";
    for(var i = 0; i < n; i++)
        rdm += Math.floor( Math.random() * 10 );
    return rdm;
}

function $stringLoad(fileName, pathName)
{
    if ( $empty(fileName) )
	{
		return "";
	}

    if ( $fragments ) 
    {
        // console.debug('HTML compiled file loaded.');
        var string = $fragments[fileName.toLowerCase()];
        if ( ! $empty(string) )
            return string;
    }

	try
	{
        var string = null;
        var url = pathName + fileName;

        $.ajax({
        	url: url,
         	async: false,
         	dataType: 'text',
         	cache: true,
         	type: 'GET',
         	success: function (data, textStatus, jqXHR)
         	{
         		string = data;
            }
        });
        return string;
	}
	catch (e)
	{
		alert("ajax string load failed: " + e.description);
	}
	return "";
}



function $templateString(str, vars)
{
    if ( ! $templateString.templateVarRegex )
    {
        $templateString.templateVarRegex = /\{%\$[a-z0-9-]+%\}/gi;
    }
    var re = $templateString.templateVarRegex;
    var items = str.match(re);
    for (var index in items)
    {
        var item = items[index];
        var key = item.substr(3, item.length - 5);
        str = str.replace(item, vars[key]);
    }
    return str;
}

function $include(fileName, div)
{
    var fgm = $stringLoad(fileName, HJ.VIEWS_PATH);
    $(fgm).appendTo(div);
}

function $serviceInvoke(service, data, async, retType, handler)
{
    var retValue = null;
    var httpMethod = data ? "POST" : "GET";
    retType = retType || "text";
    handler = handler || function(retData, textStatus, jqXHR) { retValue = retData; };
    try
    {
        var url = HJ.ACTIONS_PATH + service;
        $.ajax({
            url: url,
            async: async,
            data: data,
            dataType: retType,
            cache: false,
            type: httpMethod,
            success: handler,
        });
        return retValue;
    }
    catch (e)
    {
        alert("Service invoke failed: " + e.description);
    }
}


function $bind(receiver, func)
{
    return function(){
        func.apply(receiver, arguments);
    };
}

function $g11n(key, lang)
{
    lang = lang || 'zh-cn';
    return $strings[lang][key];
}

///////////////////////////////////////////////////////////////
// For JavaScript has a class named 'Object', 
// so use 'Base' instead of the Inherit-Root.
function Base(){
    Base._className = "Base";
    Base._superClassName = null;
}

$class(Base, null, {

    _className: null,

    constructor: function()
    {
        //console.debug('Base ctor');
    },

	super: function(func, args)
	{
		var r = func.apply(this, args);
		return r;
	},

    className: function()
    {
        return this._className;
    },

	toString: function()
	{
		return Base._className;
	}	
});
// Init Classes system.,
Base();

$class('EventHandler', Base, {
    
    addHandler: function(event, handler)
    {
        $(document).bind(event, handler);
    },
});

///////////////////////////////////////////////////////////////
//function Widget(){}

$class("Widget", [Base, EventHandler], {

    _widgetId: null,
	
	_templateString: null,

	_templateFile: null,

    _templateVars: null,

    _templateCached: false,

	_domNode: null,
	
	constructor: function(widgetId, templateFile)
	{
        if (widgetId)
        {
            this._widgetId = widgetId;
        } 
        else
        {
            var cn = this._className.toLowerCase();
            this._widgetId = cn + "-" + $random();
        }

        if (templateFile)
        {
            this._templateFile = templateFile;
            this._templateString = null;
        }
	},

	// return domNode, derived class append it to the parent.
	create: function(parent)
	{
		if (!this._templateString)
		{
			this._templateString = hj.stringLoad(this._templateFile, HJ.WIDGETS_PATH);
		}

        if (this._templateVars)
        {
            this._templateString = hj.templateString(this._templateString, this._templateVars);
        }

		this._domNode = $(this._templateString);
		return this._domNode;
	},

    widgetId: function()
    {
        return this._widgetId;
    },

    findNodes: function(sel)
    {
        return this._domNode.find(sel);
    },

    hide: function(hidden)
    {
        var dv = hidden;
        if ( hidden == false || hidden == 'false' )
        {
            dv = '';
        }
        else if ( !hidden || hidden == true || hidden == 'true' )
        {
            dv = 'none';
        }
        this._domNode.css('display', dv)
    },

    destroy: function()
    {

    },

	toString: function()
	{
		return "Widget";
	},


});
// Static Methods
Widget.addWidget = function( widget )
{
    if ( !Widget.widgetsArray )
    {
        Widget.widgetsArray = [];
    }
    Widget.widgetsArray[widget.widgetId()] = widget;
};

Widget.widgetById = function(widgetId)
{
    if ( !Widget.widgetsArray )
    {
        return null;
    }
    return Widget.widgetsArray[widgetId];
};

Widget.isWidget = function(widget)
{
    if ( typeof(widget) == 'object' )
    {
        if ( !widget._widgetId )
        {
            return false;
        }

        return true;
    }
    return false;
};

$class('ActionMixin', Base, {

    _actionBase: 'Home/',

    invokeAction: function(action, data, handler)
    {
        if (!this.invokeAction.re)
        {
            this.invokeAction.re = /(.*)Action\.(.*)/;
        }
        var re = this.invokeAction.re;
        var m = action.match(re);

        var service = this._actionBase + m[1] + '/' + m[2];
        return this.ajax(service, data, handler);
    },

    ajax: function(service, data, handler)
    {
        return $serviceInvoke(service, data, true, 'text', $bind(this, function(){
            if ( typeof(handler) == 'string' )
            {
                this[handler].apply(this, arguments);
            }
            else if ( typeof(handler) == 'function' )
            {
                handler.apply(this, arguments);
            }
        }));
    }


});


function $main(args)
{
    var t1 = new Date().getTime();
    // include all the html fragments.
    $('div[include]').each(function(){
        var div = $(this);
        var file = div.attr('include');

        $include(file, div);
    });

    $('div[widget-class]').each(function(){
        var div = $(this);
        var widgetClassName = div.attr('widget-class');
        var widgetId = div.attr('widget-id');
        var widgetClass = window[widgetClassName];
        var widget = new widgetClass(widgetId);
        widget.create(div);
        Widget.addWidget( widget );
    });

    var t2 = new Date().getTime();

    console.debug( "Main function takes " + (t2 - t1) + " ms" );

    //
}



////////////////////////////////////////////////////////////////////////////////
hj.stringLoad       = $stringLoad;
hj.templateString   = $templateString;
hj.bind             = $bind;
hj.empty            = $empty;
////////////////////////////////////////////////////////////////////////////////

String.prototype.endsWith = function(p) {
    return this.indexOf(p) + p.length == this.length;
};



